package com.yik.countforcockpit.utils;

import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class FormulaCalculateUtils {

    /**
     * 公式计算入口
     * @param formula 公式，如 "(income - cost) / quantity"
     * @param params 参数映射，如 { "income": 1000.0, "cost": 200.0, "quantity": 10.0 }
     * @return 计算结果
     */
    public static double calculate(String formula, Map<String, Double> params) {
        String replacedFormula = replaceVariables(formula, params);
        return evaluateExpression(replacedFormula);
    }

    // 替换公式中的变量为实际值
    private static String replaceVariables(String formula, Map<String, Double> params) {
        Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5a-zA-Z_][\\u4e00-\\u9fa5a-zA-Z0-9_]*");
        Matcher matcher = pattern.matcher(formula);
        StringBuilder result = new StringBuilder();
        int lastIndex = 0;
        while (matcher.find()) {
            String variable = matcher.group();
            if (params.containsKey(variable)) {
                result.append(formula, lastIndex, matcher.start());
                result.append(params.get(variable));
                lastIndex = matcher.end();
            }
        }
        result.append(formula.substring(lastIndex));
        return result.toString();
    }

    // 计算表达式的值
    private static double evaluateExpression(String expression) {
        expression = expression.replaceAll("\\s+", "");
        Stack<Double> numbers = new Stack<>();
        Stack<Character> operators = new Stack<>();

        for (int i = 0; i < expression.length(); i++) {
            char ch = expression.charAt(i);
            if (Character.isDigit(ch) || ch == '.' || ch == '%') {
                StringBuilder sb = new StringBuilder();
                // 循环匹配包含数字、小数点、百分号的内容
                while (i < expression.length() &&
                        (Character.isDigit(expression.charAt(i)) ||
                                expression.charAt(i) == '.' ||
                                expression.charAt(i) == '%')) {
                    sb.append(expression.charAt(i++));
                }
                i--;
                String numStr = sb.toString();
                // 处理百分比，例如将100%转换为1.0
                if (numStr.contains("%")) {
                    numStr = numStr.replace("%", "");
                    numbers.push(Double.parseDouble(numStr) / 100);
                } else {
                    numbers.push(Double.parseDouble(numStr));
                }
            } else if (ch == '(') {
                operators.push(ch);
            } else if (ch == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
                }
                if (!operators.isEmpty()) {
                    operators.pop();
                }
            } else if (isOperator(ch)) {
                while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(ch)) {
                    numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
                }
                operators.push(ch);
            }
        }

        while (!operators.isEmpty()) {
            numbers.push(applyOperation(operators.pop(), numbers.pop(), numbers.pop()));
        }

        return numbers.pop();
    }

    // 判断字符是否为运算符
    private static boolean isOperator(char ch) {
        return ch == '+' || ch == '-' || ch == '*' || ch == '/';
    }

    // 获取运算符的优先级
    private static int precedence(char operator) {
        switch (operator) {
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
        }
        return -1;
    }

    // 执行运算操作
    private static double applyOperation(char operator, double b, double a) {
        switch (operator) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            case '*':
                return a * b;
            case '/':
                if (b == 0) {
                    log.error("Cannot divide by zero");
                    return 0;
                }
                return a / b;
        }
        return 0;
    }

}
